﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.Diagnostics;

namespace Agent.Messaging
{
  /// <summary>
  /// Some helpers to deal with disposed wait handles
  /// </summary>
  public static class MutexExtensions
  {
    /// <summary>
    /// A version of WaitOne that copes with disposed handles
    /// </summary>
    /// <param name="handle">The handle to wait on</param>
    /// <param name="timeout">The timeout</param>
    /// <returns>The result of the wait operation</returns>
    public static WaitHandleResult SafeWaitOne(this WaitHandle handle, int timeout)
    {
      WaitHandleResult result = WaitHandleResult.Failed;
      if (handle == null)
        return WaitHandleResult.NullHandle;

      try
      {
        bool succeeded = handle.WaitOne(timeout);
        if (succeeded)
          result = WaitHandleResult.Acquired;
        else
          result = WaitHandleResult.TimedOut;
      }
      catch (ObjectDisposedException)
      {
        result = WaitHandleResult.Disposed;
      }

      return result;
    }

    /// <summary>
    /// A version of WaitAny that copes with disposed handles
    /// </summary>
    /// <param name="handles">The array of handles to wait on</param>
    /// <param name="timeout">The timeout</param>
    /// <param name="acquiredIndex">The index of the handle that was acquired, if any</param>
    /// <returns>The result of the wait operation</returns>
    public static WaitHandleResult SafeWaitAny(WaitHandle[] handles, int timeout, out int acquiredIndex)
    {
      WaitHandleResult result = WaitHandleResult.Failed;
      int tempIndex;
      acquiredIndex = -1;

      if (handles == null)
        return WaitHandleResult.NullHandle;

      if (handles.Length == 0)
        return WaitHandleResult.Failed;

      foreach (var h in handles)
        if (h == null)
          return WaitHandleResult.NullHandle;

      try
      {
        tempIndex = WaitHandle.WaitAny(handles, timeout);
        if (tempIndex == WaitHandle.WaitTimeout)
          result = WaitHandleResult.TimedOut;
        else
        {
          result = WaitHandleResult.Acquired;
          acquiredIndex = tempIndex;
        }
      }
      catch (ObjectDisposedException)
      {
        result = WaitHandleResult.Disposed;
      }

      return result;
    }

    /// <summary>
    /// A version of ReleaseMutex that copes with disposed handles
    /// </summary>
    /// <param name="mutex">The mutex to release</param>
    public static void SafeReleaseMutex(this Mutex mutex)
    {
      if (mutex == null)
        throw new ArgumentNullException("mutex");

      try
      {
        mutex.ReleaseMutex();
      }
      catch (ObjectDisposedException)
      {
        // probably OK; object is already disposed
        Debug.WriteLine("Attempt to release a mutex that was already disposed; could be an app bug");
      }
      catch
      {
        // not OK; this thread doesn't hold the mutex and is an app bug
        Debug.WriteLine("Attempt to release a mutex that wasn't held by this thread - nasty app bug");
        throw; 
      }
    }
  }
}
